package tp.aspect.counter;

import java.lang.reflect.Field;
import java.util.HashMap;

import tp.person.Person;
import tp.person.PersonGroup;

public aspect ReferenceMonitorAspect pertarget (target(tp.person.PersonGroup)) { // Preguntar por pertarget
	
	public HashMap<Person, Integer> counter = new HashMap<Person, Integer>();
	
	// Preguntar por las clases Object
	pointcut asignation(Person newValue, PersonGroup target) :
		set(tp.person.Person tp.person..*) && args(newValue) && target(target);
	
	void around(Person newValue, PersonGroup target) : asignation(newValue, target) {
		String fieldName = thisJoinPoint.getSignature().getName();
		Object oldValue = Util.getField(target, fieldName);
		
		proceed(newValue, target);
		
		this.checkReference(newValue, target, (Person) oldValue);
	}

	private void checkReference(Person newValue, PersonGroup target, Person oldValue) {
		if (!this.counter.containsKey(newValue))
			this.notifyNewReference(newValue, target);
		else {
			if (oldValue != null) {
				if (oldValue != newValue)
					this.notifyReferenceChange(newValue, target, oldValue);
				else
					this.notifyNewReference(newValue, target);
			}
			else
				this.notifyNewReference(newValue, target);
		}
	}

	private void notifyReferenceChange(Person newValue, PersonGroup target,
			Person oldValue) {
		this.notifyReferencesCount(target, newValue, this.getCurrentCountOf(newValue) + 1);
		this.notifyReferencesCount(target, oldValue, this.getCurrentCountOf(oldValue) -1);
		this.showReport(target);
	}

	private void notifyNewReference(Person newValue, PersonGroup target) {
		this.notifyReferencesCount(target, newValue, this.getCurrentCountOf(newValue) + 1);
		this.showReport(target);
	}

	private Integer getCurrentCountOf(Person value) {
		Integer count = this.counter.get(value);
		return count == null ? 0 : count;
	}

	private void notifyReferencesCount(PersonGroup target, Person value, int referencesCount) {
		this.counter.put(value, referencesCount);
		
		System.out.println("La persona de nombre " + value.getName() + " ahora tiene "
		  + referencesCount + " referencias.");
	}

	private void showReport(PersonGroup target) {
		Field[] fields = target.getClass().getFields();
		for (int i = 0; i < fields.length; i++) {
			try {
				System.out.println("Valor de la instancia " + fields[i].getName() + ": " + fields[i].get(target));
			} catch (IllegalAccessException e) {
				throw new RuntimeException(
				"Error al acceder al valor del campo actual: "
				+ target.getClass() + "." + fields[i], e);
			}
		}
		System.out.println();
	}
}
